/**
 * 遍历树形结构
 * @param data 树形结构数据
 * @param callback 回调 参数（当前节点, 父节点, 同级节点位置, 层级0开始, 同层级数据数组）
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @returns {boolean}
 */
import {isString} from "./TypeUtil";

export function treeEach(data, callback, field, parent, level = 0){
    for(let i = 0; i < data.length; i++){
        let node = data[i];
        if(callback.call(node, node, parent, i, level, data) === false){
            return false;
        }
        if (node[field] && node[field].length) {
            if(treeEach(node[field], callback, field, node, level+1) === false){
                return false;
            }
        }
    }
}

/**
 * 异步遍历树形结构 回调允许使用异步
 * @param data 树形结构数据
 * @param callback 回调 参数（当前节点, 父节点, 同级节点位置, 层级0开始, 同层级数据数组）
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @returns {boolean}
 */
export async function treeEachAsync(data, callback, field, parent, level = 0){
    for(let i = 0; i < data.length; i++){
        let node = data[i];
        if((await callback.call(node, node, parent, i, level, data)) === false){
            return false;
        }
        if (node[field] && node[field].length) {
            if((await treeEachAsync(node[field], callback, field, node, level+1)) === false){
                return false;
            }
        }
    }
}

/**
 * 遍历树结构分组
 * @param data 树结构数据
 * @param callback 回调函数, 返回组名
 * @param field 子数据在对象中的名称 默认 children
 * @returns {{}}
 */
export function treeGroup(data, callback, field, parent, level = 0){
    let result  = {};
    treeEach(data, (...args) => {
        let groupName = callback(...args),
            groupArr = result[groupName];
        if(!groupArr){
            groupArr = result[groupName] = [];
        }
        groupArr.push(args[0]);
    }, field);
    return result;
}

/**
 * 使用唯一字段生成map
 * @param data
 * @param callback 可以是唯一字段名 或 方法
 * @returns {{}}
 */
export function treeCreateMap(data, callback) {
    let result  = {},
        isS = isString(callback);
    treeEach(data, (...args) => {
        let item = args[0];
        if(isS){
            result[item[callback]] = item;
        }else{
            result[item[callback(...args)]] = item;
        }
    });
    return result;
}

/**
 * 遍历数组分组
 * @param data 数组
 * @param callback 回调函数或熟悉名, 返回组名
 * @returns {{}}
 */
export function arrayGroup(data, callback){
    let result  = {},
        isS = isString(callback);
    data.forEach((...args) => {
        let item = args[0],
            groupName = isS ? item[callback] : callback(...args),
            groupArr = result[groupName];
        if(!groupArr){
            groupArr = result[groupName] = [];
        }
        groupArr.push(args[0]);
    });
    return result;
}

/**
 * 使用唯一字段生成map
 * @param data
 * @param callback 可以是唯一字段名 或 方法
 * @returns {{}}
 */
export function arrayCreateMap(data, callback){
    let result  = {},
        isS = isString(callback);
    data.forEach((...args) => {
        let item = args[0];
        if(isS){
            result[item[callback]] = item;
        }else{
            result[item[callback(...args)]] = item;
        }
    });
    return result;
}

/**
 * 遍历树形结构，返回callback结果的组合
 * @param data 树形结构数据
 * @param callback 回调 参数（子节点返回集合, 当前节点, 父节点, 同级节点位置, 层级0开始, 同层级数据数组）
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @returns {Array} 返回组装好的数组
 */
export function treeMap(data, callback, field, parent, level = 0) {
    let result  = [];
    for(let i = 0; i < data.length; i++){
        let node = data[i],
            childResults = [];
        // 先执行子节点，获取子节点处理返回的集合
        if (node[field] && node[field].length) {
            childResults = treeMap(node[field], callback, field, node, level+1);
        }
        // 将子节点返回的集合组装
        result.push(callback.call(node, childResults, node, parent, i, level, data));
    }
    return result;
}

/**
 * 获取树形结构中所有符合条件的节点
 * @param data 树形结构数据
 * @param callback 回调 返回true | false
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @param result 符合条件的数据 递归用 直接调用不用传
 * @returns {Array}
 */
export function treeFilter(data, callback, field, parent, level = 0, result = []) {
    for(let i = 0; i < data.length; i++){
        let node = data[i];
        if(callback.call(node, node, parent, i, level, data) === true){
            result.push(node)
        }
        if (node[field] && node[field].length) {
            treeFilter(node[field], callback, field, node, level+1, result)
        }
    }
    return result;
}

/**
 * 获取树形结构中第一个符合条件的节点
 * @param data 树形结构数据
 * @param callback 回调 返回true | false
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @returns {Array}
 */
export function treeFind(data, callback, field, parent, level = 0) {
    for(let i = 0; i < data.length; i++){
        let node = data[i];
        if(callback.call(node, node, parent, i, level, data) === true){
            return node;
        }
        if (node[field] && node[field].length) {
            let result = treeFind(node[field], callback, field, node, level+1)
            if(result){
                return result;
            }
        }
    }
    return null;
}

/**
 * 获取一个节点的索引路径 如：[0,2,1,0] 第一个数字代表第一层的索引，第二个第二层... 没找到则返回null
 * @param data 树形结构数据
 * @param callback 回调 返回true | false
 * @param field 子数据在对象中的名称 默认 children
 * @param parent 父节点 递归用 直接调用不用传
 * @param level 层级 递归用 直接调用不用传
 * @param result path路径 递归用 直接调用不用传
 * @returns {Array}
 */
export function treePath(data, callback, field, parent, level = 0, result  = []){
    for(let i = 0; i < data.length; i++){
        let node = data[i];
        result.push(i);
        if(callback.call(node, node, parent, i, level, data) === true){
            return result;
        }
        if (node[field] && node[field].length) {
            if(treePath(node[field], callback, field, node, level+1, result)){
                return result
            }
        }
        result.pop();
    }
    return null;
}

export function treePathNode(data, callback, field, parent, level = 0, result = []){
    for(let i = 0; i < data.length; i++){
        let node = data[i];
        result.push(node);
        if(callback.call(node, node, parent, i, level, data) === true){
            return result;
        }
        if (node[field] && node[field].length) {
            if(treePathNode(node[field], callback, field, node, level+1, result)){
                return result
            }
        }
        result.pop();
    }
    return null;
}


